/*
 * Copyright 2023 BSC*
 * *Barcelona Supercomputing Center (BSC)
 * 
 * SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
 * 
 * Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
 * may not use this file except in compliance with the License, or, at your
 * option, the Apache License version 2.0. You may obtain a copy of the
 * License at
 * 
 * https://solderpad.org/licenses/SHL-2.1/
 * 
 * Unless required by applicable law or agreed to in writing, any work
 * distributed under the License is distributed on an “AS IS” BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

 /* Bimodal branch predictor implementation
  *
  *
  */
module bimodal_predictor
    import drac_pkg::*;
    import riscv_pkg::*;
(
    input               clk_i,                         // Clock input signal
    input               rstn_i,                        // reset input signal
    input   addrPC_t    pc_fetch_i,                    // Program counter value at Fetch Stage
    input   addrPC_t    pc_execution_i,                // Program counter at Execution Stage
    input   addrPC_t    branch_addr_result_exec_i,     // Address generated by branch in Execution Stage
    input               branch_taken_result_exec_i,    // Taken or not taken branch in Execution Stage
    input               is_branch_EX_i,                // The instruction in the Execution Stage is a branch
    output              bimodal_predict_taken_o,       // Bit that encodes branch taken '1' or not '0'
    output  addrPC_t    bimodal_predict_addr_o         // Address predicted to jump
);

// Length of the bimodal index register
localparam _LENGTH_BIMODAL_INDEX_  = 7;
// Number of entries of the bimodal predictor, must be 2^(_LENGTH_BIMODAL_INDEX_)
localparam _NUM_BIMODAL_ENTRIES_ = 2**_LENGTH_BIMODAL_INDEX_;
// Number of bits used for encoding the state of predictor state machine
localparam _BITS_BIMODAL_STATE_MACHINE_ = 2;
// Initial state of the predictor state machine
localparam _INITIAL_STATE_BIMODAL_ = 2'b10;

logic [_BITS_BIMODAL_STATE_MACHINE_-1:0] new_state_to_pht;
logic [_BITS_BIMODAL_STATE_MACHINE_-1:0] readed_state_pht;
logic [1:0] past_state_pht;

    // Creates an array of _NUM_BIMODAL_ENTRIES_ registers of _BITS_BIMODAL_STATE_MACHINE_ length
    // This array stores 1024 states machines for predicting the branches

    reg [_BITS_BIMODAL_STATE_MACHINE_ -1:0] pattern_history_table [0:_NUM_BIMODAL_ENTRIES_ -1]; 
    reg [ADDR_SIZE-1:0] branch_target_buffer [0:_NUM_BIMODAL_ENTRIES_-1];

    
 /*   `ifndef SYNTHESIS_BIMODAL_PREDICTOR
        // Initialize all the entries of the pattern history table with the initial state
        integer i;
        initial 
        begin for(i = 0; i < _NUM_BIMODAL_ENTRIES_ ; i = i + 1) begin
                pattern_history_table[i] = _INITIAL_STATE_BIMODAL_;
                branch_target_buffer[i] = 40'h0;
              end
        end
    `endif*/


    // Read pattern history table at addres pc_fetch_i
    logic [ADDR_SIZE-1:0] short_pred_addr;
    always_comb
    begin
        readed_state_pht = pattern_history_table[pc_fetch_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]];
        past_state_pht = pattern_history_table[pc_execution_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]];
        short_pred_addr = branch_target_buffer[pc_fetch_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]];
        bimodal_predict_addr_o = { {XLEN-ADDR_SIZE{short_pred_addr[ADDR_SIZE-1]}}, short_pred_addr};
    end
   
    always_comb begin
        if (past_state_pht == 2'b00 && branch_taken_result_exec_i == 1'b0)
            new_state_to_pht = 2'b00;
        else if (past_state_pht == 2'b11 && branch_taken_result_exec_i == 1'b1)
            new_state_to_pht = 2'b11;
        else if (branch_taken_result_exec_i == 1'b1)
            new_state_to_pht = past_state_pht + 2'b01;
        else
            new_state_to_pht = past_state_pht - 2'b01;
    end


    // Write pattern history table at addres pc_fetch_i
    always@(posedge clk_i or negedge rstn_i ) begin 
		if (~rstn_i) begin
			for(integer i = 0; i < _NUM_BIMODAL_ENTRIES_ ; i = i + 1) begin
				pattern_history_table[i] <= 2'b01;
			end
        end else if(is_branch_EX_i) begin 
            pattern_history_table[pc_execution_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]] <= new_state_to_pht;
            branch_target_buffer[pc_execution_i[MOST_SIGNIFICATIVE_INDEX_BIT_BP:LEAST_SIGNIFICATIVE_INDEX_BIT_BP]] <= branch_addr_result_exec_i;
		end
    end    
    // If state is 00 or 01 predict not taken, if 10 or 11 predict taken
    assign bimodal_predict_taken_o = readed_state_pht[1];

endmodule
